#pragma once

#include <boostext/iostreams/bin_iostream.hpp>

#include "Queue.hpp"




namespace Engine
{
	namespace Debugger
	{
		/**
		 * This class defines a full duplex queue that uses a defined device to write
		 * and read elements of the defined type.
		 *
		 * The device can be anything that models a read/write data source/sink concept.
		 *
		 * An example to use this class would be to create two instances:
		 * duplex_queue<Command, Response, Response::Type>
		 * duplex_queue<Response, Command, Command::Type>
		 *
		 * And then somehow connect both streams so that writing a command using the one stream
		 * would enable the second one to read it. This can be done by using a tcp_stream as the
		 * underlying device and connect it with another stream.
		 */
		template <typename Elem>
		class duplex_queue
			: public Queue<Elem, Direction::WriteOnly>
			, public Queue<Elem, Direction::ReadOnly>
		{
		public:

			SHARED(duplex_queue<Elem>);

			typedef duplex_queue<Elem>          this_t;
			typedef boost::shared_ptr<this_t>   ptr;



			/**
			 * Creates a duplex_queue, using the given stream to read/write elements.
			 */
			duplex_queue(boostext::iostreams::bin_iostream& stream)
				: m_stream(stream)
			{}

			virtual ~duplex_queue()
			{}



			/**
			 * Writes the given element into the queue.
			 * In case the queue is full, the function blocks
			 * until the element can be written into the queue.
			 *
			 * @throws queue_full in case no more elements can be added to the queue
			 */
			virtual Queue<Elem, Direction::WriteOnly>& operator<<(const Elem& elem)
			{
				m_stream << elem;
				return *this;
			}

			/**
			 * Reads an element from the queue. Due to the polymorphic implementation
			 * of elements, the actual element is stored in a shared_ptr.
			 * Doesn't block if the queue is empty, instead the ptr is left untouched.
			 *
			 * @throws in case the queue is WriteOnly
			 */
			virtual Queue<Elem, Direction::ReadOnly>& operator>>(boost::shared_ptr<Elem>& elem)
			{
				m_stream >> elem;
				return *this;
			}

		private:

			boostext::iostreams::bin_iostream&   m_stream;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
